using System;
using System.IO;

namespace Narwhunderful.Aphelia.Framework.Security.Cryptography
{

    public abstract class HashAlgorithm : ICryptoTransform, IDisposable
    {

        int _hashSizeValue;
        public int HashSize
        {
            get
            {
                return _hashSizeValue;
            }
            protected set
            {
                _hashSizeValue = value;
            }
        }

        byte[] _hashValue;
        protected internal byte[] HashValue
        {
            get
            {
                return _hashValue;
            }
            set
            {
                _hashValue = value;
            }
        }

        int _state;
        protected int State
        {
            get
            {
                return _state;
            }
            set
            {
                _state = value;
            }
        }

        private bool _disposed;

        public virtual bool CanReuseTransform
        {
            get
            {
                return true;
            }
        }

        public virtual bool CanTransformMultipleBlocks
        {
            get
            {
                return true;
            }
        }

        public virtual byte[] Hash
        {
            get
            {
                if (_disposed)
                {
                    throw new ObjectDisposedException(null);
                }
                if (_state != 0)
                {
                    throw new Exception("Cryptography HashNotYetFinalized");
                }
                return (byte[])this._hashValue.Clone();
            }
        }

        public virtual int InputBlockSize
        {
            get
            {
                return 1;
            }
        }

        public virtual int OutputBlockSize
        {
            get
            {
                return 1;
            }
        }

        public HashAlgorithm()
        {
        }

        public void Clear()
        {
            ((IDisposable)this).Dispose();
        }

        public byte[] ComputeHash(byte[] buffer)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(null);
            }
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            this.HashCore(buffer, 0, buffer.Length);
            _hashValue = this.HashFinal();
            byte[] buffer2 = (byte[])_hashValue.Clone();
            this.Initialize();
            return buffer2;
        }

        public byte[] ComputeHash(Stream inputStream)
        {
            int num;
            if (_disposed)
            {
                throw new ObjectDisposedException(null);
            }
            byte[] buffer = new byte[0x1000];
            do
            {
                num = inputStream.Read(buffer, 0, 0x1000);
                if (num > 0)
                {
                    this.HashCore(buffer, 0, num);
                }
            }
            while (num > 0);
            _hashValue = this.HashFinal();
            byte[] buffer2 = (byte[])_hashValue.Clone();
            this.Initialize();
            return buffer2;
        }

        public byte[] ComputeHash(byte[] buffer, int offset, int count)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset (value has to be non negative)");
            }
            if ((count < 0) || (count > buffer.Length))
            {
                throw new ArgumentException("invalid value");
            }
            if ((buffer.Length - count) < offset)
            {
                throw new ArgumentException("invalid off len");
            }
            if (_disposed)
            {
                throw new ObjectDisposedException(null);
            }
            this.HashCore(buffer, offset, count);
            _hashValue = this.HashFinal();
            byte[] buffer2 = (byte[])_hashValue.Clone();
            this.Initialize();
            return buffer2;
        }

        protected abstract void HashCore(byte[] array, int ibStart, int cbSize);
        protected abstract byte[] HashFinal();
        public abstract void Initialize();

        public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
        {
            if (inputBuffer == null)
            {
                throw new ArgumentNullException("inputBuffer");
            }
            if (inputOffset < 0)
            {
                throw new ArgumentOutOfRangeException("inputOffset", "ArgumentOutOfRange_NeedNonNegNum");
            }
            if ((inputCount < 0) || (inputCount > inputBuffer.Length))
            {
                throw new ArgumentException("Argument_InvalidValue");
            }
            if ((inputBuffer.Length - inputCount) < inputOffset)
            {
                throw new ArgumentException("Argument_InvalidOffLen");
            }
            if (this._disposed)
            {
                throw new ObjectDisposedException(null);
            }
            this._state = 1;
            this.HashCore(inputBuffer, inputOffset, inputCount);
            if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset)))
            {
                Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
            }
            return inputCount;
        }

        public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
        {
            if (inputBuffer == null)
            {
                throw new ArgumentNullException("inputBuffer");
            }
            if (inputOffset < 0)
            {
                throw new ArgumentOutOfRangeException("inputOffset", "ArgumentOutOfRange_NeedNonNegNum");
            }
            if ((inputCount < 0) || (inputCount > inputBuffer.Length))
            {
                throw new ArgumentException("Argument_InvalidValue");
            }
            if ((inputBuffer.Length - inputCount) < inputOffset)
            {
                throw new ArgumentException("Argument_InvalidOffLen");
            }
            if (this._disposed)
            {
                throw new ObjectDisposedException(null);
            }
            HashCore(inputBuffer, inputOffset, inputCount);
            _hashValue = this.HashFinal();
            byte[] dst = new byte[inputCount];
            if (inputCount != 0)
            {
                Buffer.BlockCopy(inputBuffer, inputOffset, dst, 0, inputCount);
            }
            _state = 0;
            return dst;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_hashValue != null)
                {
                    Array.Clear(_hashValue, 0, _hashValue.Length);
                }
                _hashValue = null;
                _disposed = true;
            }
        }

        void IDisposable.Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

    }
}

